home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Cream of the Crop 1
/
Cream of the Crop 1.iso
/
PROGRAM
/
UUPC11QS.ARJ
/
COMMFIFO.ASM
< prev
next >
Wrap
Assembly Source File
|
1991-12-08
|
44KB
|
1,445 lines
TITLE COMM
PAGE 75,132
; Use AHD's handling of COM ports. Wm. W. Plummer, 11/29/91
; Buffer sizes reduced and required to be 2**N. Wm. W. Plummer, 11/11/91
; Accomodate V.24 requirements on DTR flaps. Wm. W. Plummer 10/15/91
; Revised DTR_ON_COM to solve user problem. Wm. W. Plummer, 10/3/91
; Make time delays independent of CPU speed. Wm. W. Plummer, 9/16/91
; Use interrupts to trace CD, DSR, Wm. W. Plummer, 9/16/91
; Remove modem control from TX_INT. Wm. W. Plummer, 9/13/91
; Completely redo the XOFF/XON logic. Too many races before. Wm. W. Plummer
; Revise interrupt dispatch for speed & function. William W. Plummer, 9/12/91
; Merge in ahd's changes to flush control Q,S when received as flow control
; SEND buffer allows one byte for a SENDII call. Avoids flow control
; lockups. - William W. Plummer, 8/30/91
; Support for NS16550A chip with SILO - William W. Plummer, 8/30/91
; Add modem_status() routine - William W. Plummer, 7/2/91
; Put wrong code under AHD conditional - William W. Plummer, 7/2/91
; Change TITLE, repair bad instr after INST3 - William W. Plummer, 7/1/91
; Modified to use COM1 thru COM4 - William W. Plummer, 2/21/91
; Eliminate (incomplete) support for DOS1 - William W. Plummer, 11/13/90
; Changes may be copied and modified with no notice. Copyrights and copylefts
; are consider silly and do not apply. -- William W. Plummer
; modified to use MSC calling sequence. jrr 3/86
;****************************************************************************
; Communications Package for the IBM PC, XT, AT, PCjr and strict compatibles.
; May be copied and used freely -- This is a public domain program
; Developed by Richard Gillmann, John Romkey, Jerry Saltzer,
; Craig Milo Rogers, Dave Mitton and Larry Afrin.
;
; We'd sure like to see any improvements you might make.
; Please send all comments and queries about this package
; to GILLMANN@USC-ISIB.ARPA
;
; o Supports both serial ports simultaneously
; o All speeds to 19200 baud
; o Compatible with PC, XT, AT and PCjr.
; o Built in XON/XOFF flow control option
; o Assembly language calling conventions
; o Logs all comm errors
; o Direct connect or modem protocol
PAGE ;
;
; MAXIMUM BUFFER SIZES -- *** MUST be powers of 2 ****
R_SIZE EQU 1024 ; Recv buffer size
S_SIZE EQU 256 ; Send buffer size
; INTERRUPT NUMBERS
INT_COM1 EQU 0CH ; COM1: FROM 8259
INT_COM2 EQU 0BH ; COM2: FROM 8259
INT_COM3 EQU 0CH ; COM3: FROM 8259
INT_COM4 EQU 0BH ; COM4: FROM 8259
; 8259 PORTS
INTA00 EQU 20H ; 8259A PORT, A0 = 0
INTA01 EQU 21H ; 8259A PORT, A0 = 1
; COM1: & COM3: LEVEL 4
IRQ4 EQU 2*2*2*2 ; 8259A OCW1 MASK, M4=1, A0=0
NIRQ4 EQU NOT IRQ4 AND 0FFH ; COMPLEMENT OF ABOVE
EOI4 EQU 4 OR 01100000B ; 8259A OCW2 SPECIFIC IRQ4 EOI, A0=0
; COM2: & COM4: LEVEL 3
IRQ3 EQU 2*2*2 ; 8259A OCW1 MASK, M3=1, A0=0
NIRQ3 EQU NOT IRQ3 AND 0FFH ; COMPLEMENT OF ABOVE
EOI3 EQU 3 OR 01100000B ; 8259A OCW2 SPECIFIC IRQ3 EOI, A0=0
; FLOW CONTROL CHARACTERS
CONTROL_Q EQU 11H ; XON
CONTROL_S EQU 13H ; XOFF
; MISC.
DOS EQU 21H ; DOS FUNCTION CALLS
PAGE
;
; ROM BIOS Data Area
;
RBDA SEGMENT AT 40H
RS232_BASE DW 4 DUP(?) ; ADDRESSES OF RS232 ADAPTERS
RBDA ENDS
;
; ROM PC-Type IDENT
;
ROM SEGMENT AT 0F000H
ORG 0FFFEH
ROMID DB ? ; 0FFH=PC OR EARLY XT, 0FEH=XT, 0FDH=JR
ROM ENDS
PAGE ;
;
; TABLE FOR EACH SERIAL PORT
;
SP_TAB STRUC
PORT DB ? ; 1 OR 2 OR 3 OR 4
; PARAMETERS FOR THIS INTERRUPT LEVEL
INT_COM DB ? ; INTERRUPT NUMBER
IRQ DB ? ; 8259A OCW1 MASK
NIRQ DB ? ; COMPLEMENT OF ABOVE
EOI DB ? ; 8259A OCW2 SPECIFIC END OF INTERRUPT
; INTERRUPT HANDLERS FOR THIS LEVEL
INT_HNDLR DW ? ; OFFSET TO INTERRUPT HANDLER
OLD_COM_OFF DW ? ; OLD HANDLER'S OFFSET
OLD_COM_SEG DW ? ; OLD HANDLER'S SEGMENT
; ATTRIBUTES
INSTALLED DB ? ; IS PORT INSTALLED ON THIS PC? (1=YES,0=NO)
BAUD_RATE DW ? ; 19200 MAX
CONNECTION DB ? ; M(ODEM), D(IRECT)
PARITY DB ? ; N(ONE), O(DD), E(VEN), S(PACE), M(ARK)
STOP_BITS DB ? ; 1, 2
XON_XOFF DB ? ; E(NABLED), D(ISABLED)
; FLOW CONTROL STATE
HOST_OFF DB ? ; HOST XOFF'ED (1=YES,0=NO)
PC_OFF DB ? ; PC XOFF'ED (1=YES,0=NO)
URGENT_SEND DB ? ; We MUST send one byte (XON/XOFF)
SEND_OK DB ? ; DSR and CTS are ON
; ERROR COUNTS
ERROR_BLOCK DW 8 DUP(?); EIGHT ERROR COUNTERS
; UART PORTS - DATREG thru MSR must be in order shown.
DATREG DW ? ; DATA REGISTER
IER DW ? ; INTERRUPT ENABLE REGISTER
IIR DW ? ; INTERRUPT IDENTIFICATION REGISTER (RO)
LCR DW ? ; LINE CONTROL REGISTER
MCR DW ? ; MODEM CONTROL REGISTER
LSR DW ? ; LINE STATUS REGISTER
MSR DW ? ; MODEM STATUS REGISTER
FCR EQU IIR ; FIFO Control Register (WO)
; Bits in FCR for NS16550A UART. Note that writes to FCR are ignored
; by other chips.
FIFO_ENABLE EQU 001H ; Enable FIFO mode
FIFO_CLR_RCV EQU 002H ; Clear receive FIFO
FIFO_CLR_XMT EQU 004H ; Clear transmit FIFO
FIFO_STR_DMA EQU 008H ; Start DMA Mode
; 10H and 20H bits are register bank select on some UARTs (not handled)
FIFO_SZ_4 EQU 040H ; Warning level is 4 before end
FIFO_SZ_8 EQU 080H ; Warning level is 8 before end
FIFO_SZ_14 EQU 0C0H ; Warning level is 14 before end
FIFO_SZ_MASK EQU 0C0H ; Size mask
;
; Commands used in code to operate FIFO. Made up as combinations of above
;
FIFO_CLEAR EQU 0 ; Turn off FIFO
FIFO_SETUP EQU FIFO_SZ_14 OR FIFO_ENABLE
FIFO_INIT EQU FIFO_SETUP OR FIFO_CLR_RCV OR FIFO_CLR_XMT
;
; Miscellaneous FIFO-related stuff
;
FIFO_ENABLED EQU 0C0H ; 16550 makes these equal FIFO_ENABLE
FIFO_LEN EQU 16 ; Length of the FIFOs in a 16550A
UART_SILO_LEN DB ? ; Size of a silo chunk (1 for 8250)
;
; BUFFER POINTERS
START_TDATA DW ? ; INDEX TO FIRST CHARACTER IN X-MIT BUFFER
END_TDATA DW ? ; INDEX TO FIRST FREE SPACE IN X-MIT BUFFER
START_RDATA DW ? ; INDEX TO FIRST CHARACTER IN REC. BUFFER
END_RDATA DW ? ; INDEX TO FIRST FREE SPACE IN REC. BUFFER
; BUFFER COUNTS
SIZE_TDATA DW ? ; NUMBER OF CHARACTERS IN X-MIT BUFFER
SIZE_RDATA DW ? ; NUMBER OF CHARACTERS IN REC. BUFFER
; BUFFERS
TDATA DB S_SIZE DUP(?) ; SEND BUFFER
RDATA DB R_SIZE DUP(?) ; RECEIVE BUFFER
SP_TAB ENDS
; SP_TAB EQUATES
; WE HAVE TO USE THESE BECAUSE OF PROBLEMS WITH STRUC
EOVFLOW EQU ERROR_BLOCK ; BUFFER OVERFLOWS
EOVRUN EQU ERROR_BLOCK+2 ; RECEIVE OVERRUNS
EBREAK EQU ERROR_BLOCK+4 ; BREAK CHARS
EFRAME EQU ERROR_BLOCK+6 ; FRAMING ERRORS
EPARITY EQU ERROR_BLOCK+8 ; PARITY ERRORS
EXMIT EQU ERROR_BLOCK+10 ; TRANSMISSION ERRORS
EDSR EQU ERROR_BLOCK+12 ; DATA SET READY ERRORS
ECTS EQU ERROR_BLOCK+14 ; CLEAR TO SEND ERRORS
DLL EQU DATREG ; LOW DIVISOR LATCH
DLH EQU IER ; HIGH DIVISOR LATCH
PAGE ;
; put the data in the DGROUP segment
; far calls enter with DS pointing to DGROUP
;
DGROUP GROUP _DATA
_DATA SEGMENT PUBLIC 'DATA'
;
DIV50PC EQU 2304 ; DIVISOR FOR 50 BAUD (PC,XT)
DIV50JR EQU 2237 ; DIVISOR FOR 50 BAUD (JR)
DIV50 DW 2304 ; ACTUAL DIVISOR FOR 50 BAUD IN USE
CURRENT_AREA DW AREA1 ; CURRENTLY SELECTED AREA
; DATA AREAS FOR EACH PORT
AREA1 SP_TAB <1,INT_COM1,IRQ4,NIRQ4,EOI4> ; COM1 DATA AREA
AREA2 SP_TAB <2,INT_COM2,IRQ3,NIRQ3,EOI3> ; COM2 DATA AREA
AREA3 SP_TAB <3,INT_COM3,IRQ4,NIRQ4,EOI4> ; COM3 DATA AREA
AREA4 SP_TAB <4,INT_COM4,IRQ3,NIRQ3,EOI3> ; COM4 DATA AREA
_DATA ENDS
PAGE ;
COM_TEXT SEGMENT PARA PUBLIC 'CODE'
ASSUME CS:COM_TEXT,DS:DGROUP,ES:NOTHING
PUBLIC _select_port
PUBLIC _save_com
PUBLIC _install_com
PUBLIC _restore_com
PUBLIC _open_com
PUBLIC _close_com
PUBLIC _dtr_on
PUBLIC _dtr_off
PUBLIC _r_count
PUBLIC _s_count
PUBLIC _receive_com
PUBLIC _send_com
PUBLIC _sendi_com
PUBLIC _send_local
PUBLIC _break_com
PUBLIC _com_errors
PUBLIC _modem_status
IFDEF DEBUG
PUBLIC INST2, INST4
PUBLIC OC1, OC2, OCX
PUBLIC DTRON1, DTRON6, DTRONF, DTRONS, DTRONX
PUBLIC L11, LI3, LI4
PUBLIC RCVX
PUBLIC L4A, L44
PUBLIC WaitN, WaitN1, WaitN2
PUBLIC SENDII, SENDII2, SENDII3, SENDII4, SENDIIX
PUBLIC INT_COMMON, REPOLL, INT_END
PUBLIC LSTAT_INT
PUBLIC MSTAT_INT
PUBLIC TX_INT, TXI1, TXI2, TXI3, TXI9
PUBLIC RX_INT, RXI0, RXI1, RXI2, RXI6, RXIX
ENDIF
PAGE ;
; Notes, thoughts and explainations by Bill Plummer. These are intended to
; help those of you who would like to make modifications.
; Here's the order of calls in UUPC. The routines in COMM.ASM are called
; from ulib.c.
; First (when a line in system has been read?), ulib&openline calls
; select_port() ; Sets up CURRENT_AREA
; then, save_com() ; Save INT vector
; then, install_com() ; Init area, hook INT
; then, open_com(&cmd, 'D', 'N', STOP*T, 'D') ; Init UART, clr bufs
; then, dtr_on().
; At that point the line is up and running. UUPC calls ulib&sread()
; which calls, receive_com();
; And UUPC calls ulib&swrite()
; which calls, send_com();
; To cause an error that the receiver will see, UUPC calls ulib&ssendbrk();
; which calls, break_com();
; When all done with the line, UUPC calls ulib&closeline()
; which calls, dtr_off();
; then, close_com();
; then, restore_com(); ; Unhook INT
; and, stat_errors();
; Note: On the PC COM1 and COM3 share IRQ4, while COM2 and COM4 share IRQ3.
; BUT, only one device on a given IRQ line can be active at a time. So it is
; sufficient for UUPC to hook whatever IRQ INT its modem is on as long as it
; unhooks it when it is done with that COM port. COMM cannot be an installed
; device driver since it must go away when UUPC is done so that other devices
; on the same INTs will come back to life. Also, it is OK to have a static
; CURRENT_AREA containing the current area that UUPC is using.
; Note about using the NS16550A UART chip's FIFOs. These are operated as
; silos. In other words when an interrupt happens because the receive(send)
; FIFO is nearly full(empty), as many bytes as possible are transferred and
; the interrupt dismissed. Thus, the interrupt load is lowered.
; Concerning the way the comm line is brought up.
; There are two basic cases, the Direct ('D') connection and the Modem ('M')
; connection. For either UUPC calls dtr_on_com() to bring up the line. This
; causes Data Terminal Ready (DTR) and Request To Send (RTS) to be set. Note
; this is OK for a simple 3-wire link but may be REQUIRED for a COM port
; connected to an external modem.
; The difference between a D connection and an M connection is
; whether or not the PC can expect any signals back from the modem. If
; there is a simple 3-wire link, Data Set Ready will be floating.
; (Actually, some wise people jumper Data Terminal Read back to Data
; Set Ready so the PC sees its own DTR appear as DSR.) UUPC should be
; able to handle the simplest cable as a design feature. So both D and
; M connections send out DTR and RTS, but only the M connection expects
; a modem to respond.
; Then, if it is full modem connection (M), we wait for a few
; seconds hoping that both Data Set Ready (DSR) and Clear To Send (CTS)
; will come up. If they don't, the associated counters are incremented
; for subsequent printing in the error log. Note that no error is
; reported from COMM to UUPC at this point, although this would be a
; good idea. Instead UUPC storms ahead with its output trying to
; establish a link, but the output is never sent due to one of the
; control signals being false. UUPC could check the modem status using
; a call which has been installed just for this purpose.
; Note, if you are going to connect your PC running UUPC to,
; say, a mainframe and you need hardware flow control (i.e., RTS-CTS
; handshaking), use a Modem connection. Using a simple 3-wire cable
; forbids hardware flow control and UUPC must be instructed to use a
; Direct connection. Refer to comments in the sample MODEM file (*.MDM)
; on how to make this selection.
; References used in designing the revisions to COMM.ASM:
; 1. The UNIX fas.c Driver code.
; 2. SLIP8250.ASM from the Clarkson driver set.
; 3. NS16550A data sheet and AN-491 from National Semiconductor.
; 4. Bell System Data Communications, Technical Reference for
; Data Set 103A, Interface Specification, February, 1967
; 5. Network mail regarding V.24
; 6. Joe Doupnik, Utah State, primary author of MS-Kermit
PAGE ;
;
; SELECT WHICH PORT IS TO BE "ACTIVE"
; [bp+6] = port number
_select_port PROC FAR
push bp
mov bp,sp
mov AX,[bp+6] ; get aguement
CMP AL,1 ; Port 1?
JE SP1 ; Yes
CMP AL,2 ; Port 2?
JE SP2 ; Yes
CMP AL,3 ; Port 3?
JE SP3 ; Yes
CMP AL,4 ; Port 4?
JE SP4 ; Yes
INT 20H ; N.O.T.A. ????? Halt for debugging!
; Assume port 1 if continued
SP1: MOV AX,OFFSET DGROUP:AREA1 ; SELECT COM1 DATA AREA
JMP SHORT SPX ; CONTINUE
SP2: MOV AX,OFFSET DGROUP:AREA2 ; SELECT COM2 DATA AREA
JMP SHORT SPX ; CONTINUE
SP3: MOV AX,OFFSET DGROUP:AREA3 ; SELECT COM3 DATA AREA
JMP SHORT SPX ; CONTINUE
SP4: MOV AX,OFFSET DGROUP:AREA4 ; SELECT COM4 DATA AREA
;Fall into SPX
SPX: MOV CURRENT_AREA,AX ; SET SELECTION IN MEMORY
mov sp,bp
pop bp
RET
_select_port ENDP
PAGE ;
;
; SAVE ORIGINAL COM VECTOR
;
_save_com PROC FAR
push bp
mov bp,sp
push si
PUSH ES ; SAVE EXTRA SEGMENT
MOV SI,CURRENT_AREA ; SI POINTS TO DATA AREA
MOV AREA1.INT_HNDLR,OFFSET INT_HNDLR1
MOV AREA2.INT_HNDLR,OFFSET INT_HNDLR2
MOV AREA3.INT_HNDLR,OFFSET INT_HNDLR3
MOV AREA4.INT_HNDLR,OFFSET INT_HNDLR4
; Save old interrupt vector
MOV AH,35H ; FETCH INTERRUPT VECTOR CONTENTS
MOV AL,INT_COM[SI] ; INTERRUPT NUMBER
INT DOS ; DOS 2 FUNCTION
MOV OLD_COM_OFF[SI],BX ; SAVE
MOV BX,ES ; ES:BX
MOV OLD_COM_SEG[SI],BX ; FOR LATER RESTORATION
POP ES ; RESTORE ES
pop si
mov sp,bp
pop bp
RET ; DONE
_save_com ENDP
PAGE ;
;
; INSTALL_COM: INSTALL THE ACTIVE PORT
;
; SET UART PORTS FROM RS-232 BASE IN ROM BIOS DATA AREA
; INITIALIZE PORT CONSTANTS AND ERROR COUNTS
; INSTALL INTERRUPT VECTOR
;
; return ax=1 on success ax=0 on failure
;
_install_com PROC FAR
push bp
mov bp,sp
push si
PUSH BX
PUSH CX
PUSH DX
MOV SI,CURRENT_AREA ; SI POINTS TO DATA AREA
PUSH ES ; SAVE EXTRA SEGMENT
CMP INSTALLED[SI],1 ; Is port installed on this machine?
JNE INSTOK ; NO, CONTINUE
JMP INST9 ; ELSE JUMP IF ALREADY INSTALLED
;
; CLEAR ERROR COUNTS
INSTOK: MOV WORD PTR EOVFLOW[SI],0 ; BUFFER OVERFLOWS
MOV WORD PTR EOVRUN[SI],0 ; RECEIVE OVERRUNS
MOV WORD PTR EBREAK[SI],0 ; BREAK CHARS
MOV WORD PTR EFRAME[SI],0 ; FRAMING ERRORS
MOV WORD PTR EPARITY[SI],0 ; PARITY ERRORS
MOV WORD PTR EXMIT[SI],0 ; TRANSMISSION ERRORS
MOV WORD PTR EDSR[SI],0 ; DATA SET READY ERRORS
MOV WORD PTR ECTS[SI],0 ; CLEAR TO SEND ERRORS
; SENSE PC TYPE AND SET DIVISOR ACCORDINGLY
MOV BX,ROM ; HIGH ROM SEGMENT
MOV ES,BX ; TO ES
ASSUME ES:ROM
MOV DIV50,DIV50PC ; ASSUME PC OR XT
CMP ROMID,0FDH ; IS IT A PCjr?
JNE INST0 ; JUMP IF NOT
MOV DIV50,DIV50JR ; ELSE SET JR DIVISOR
; SET UART PORT ADDRESSES
INST0: MOV BX,RBDA ; ROM BIOS DATA AREA
MOV ES,BX ; TO ES
ASSUME ES:RBDA
;
; Map port number (COMx) into IO Address using the RS232_Base[x] table in
; the BIOS data area. If any of the ports is missing there should be a
; zero in the table for this COM port. BIOS startup routines pack the table
; so that if you have a COM4 but no COM3, 2E8 will be found in 40:4 and 0
; will be in 40:6.
; N.B. The exact IO address in 40:x is irrelevant and may well be something
; other than the "standard" values if specially designed hardware is used.
; To minimize flack, we will use the standard value if the slot in the table
; is 0. The bad side effect of this is that (in the standard losing case of
; a COM4 but no COM3) both COM3 and COM4 will reference the hardware at 2E8.
CMP PORT[SI],1 ; PORT 1?
JE INST3F8 ; Yes
CMP PORT[SI],2 ; PORT 2?
JE INST2F8 ; Yes
CMP PORT[SI],3 ; PORT 3?
JE INST3E8 ; Yes
CMP PORT[SI],4 ; PORT 4?
JE INST2E8 ; Yes
INT 20H ; NOTA. (Caller is screwed up badly)
INST3F8:MOV AX,3F8H ; Standard COM1 location
CMP RS232_BASE+0,0000H ; We have information?
JE INST2 ; No --> Use default
MOV AX,RS232_BASE+0 ; Yes --> Use provided info
JMP SHORT INST2 ; CONTINUE
INST2F8:MOV AX,2F8H ; Standard COM2 location
CMP RS232_BASE+2,0000H ; We have information?
JE INST2 ; No --> Use default
MOV AX,RS232_BASE+2 ; Yes --> Use provided info
JMP SHORT INST2 ; CONTINUE
INST3E8:MOV AX,3E8H ; Standard COM3 location
CMP RS232_BASE+4,0000H ; We have information?
JE INST2 ; No --> Use default
MOV AX,RS232_BASE+4 ; Yes --> Use provided info
JMP SHORT INST2 ; CONTINUE
INST2E8:MOV AX,2E8H ; Standard COM4 location
CMP RS232_BASE+6,0000H ; We have information?
JE INST2 ; No --> Use default
MOV AX,RS232_BASE+6 ; Yes --> Use provided info
; Fall into INST2
; Now we have an IO address for the COMx that we want to use. If it is
; anywhere in RS232_Base, we know that it has been check and is OK to use.
; So, even if my 2E8 (COM4) appears in 40:6 (normally for COM3), I can use
; it.
INST2: CMP AX,RS232_BASE ; INSTALLED?
JE INST2A ; JUMP IF SO
CMP AX,RS232_BASE+2 ; INSTALLED?
JE INST2A ; JUMP IF SO
CMP AX,RS232_BASE+4 ; INSTALLED?
JE INST2A ; JUMP IF SO
CMP AX,RS232_BASE+6 ; INSTALLED?
JNE INST666 ; JUMP IF NOT
; Fall into INST2A
INST2A: MOV BX,DATREG ; OFFSET OF TABLE OF PORTS
MOV CX,7 ; LOOP SIX TIMES
INST3: MOV WORD PTR [SI][BX],AX ; SET PORT ADDRESS
INC AX ; NEXT PORT
ADD BX,2 ; NEXT WORD ADDRESS
LOOP INST3 ; RS232 BASE LOOP
MOV DX,FCR[SI] ; Get FIFO Control Register
MOV AL,FIFO_INIT
OUT DX,AL ; Try to initialize the FIFO
MOV DX,IIR[SI] ; Get interrupt ID register
IN AL,DX ; See how the UART responded
AND CL,FIFO_ENABLED ; Keep only these bits
MOV CX,1 ; Assume chunk size of 1 for 8250 case
CMP AL,FIFO_ENABLED ; See if 16550A
JNE INST4 ; Jump if not
IFE (FIFO_SETUP AND FIFO_SZ_MASK)
MOV CX,FIFO_LEN-FIFO_LEN
ELSEIFE (FIFO_SETUP AND FIFO_SZ_MASK - FIFO_SZ_4)
MOV CX,FIFO_LEN-4
ELSEIFE (FIFO_SETUP AND FIFO_SZ_MASK - FIFO_SZ_8)
MOV CX,FIFO_LEN-8
ELSEIFE (FIFO_SETUP AND FIFO_SZ_MASK - FIFO_SZ_14)
MOV CX,FIFO_LEN-14
ENDIF
INST4: MOV UART_SILO_LEN[SI],CL ; Save chunk size
MOV AL,FIFO_CLEAR
OUT DX,AL
MOV AH,25H ; SET INTERRUPT VECTOR CONTENTS
MOV AL,INT_COM[SI] ; INTERRUPT NUMBER
MOV DX,INT_HNDLR[SI] ; OUR INTERRUPT HANDLER [WWP]
PUSH DS ; SAVE DATA SEGMENT
PUSH CS ; COPY CS
POP DS ; TO DS
INT DOS ; DOS FUNCTION
POP DS ; RECOVER DATA SEGMENT
; PORT INSTALLED
INST9: MOV AX,1
JMP SHORT INSTX
; PORT NOT INSTALLED
INST666:MOV AX,0
;Fall into INSTX
; Common exit
INSTX: MOV INSTALLED[SI],AL ; Indicate whether installed or not
POP DX
POP CX
POP BX
POP ES
pop si
mov sp,bp
pop bp
RET
_install_com ENDP
PAGE ;
;
; RESTORE ORIGINAL INTERRUPT VECTOR
;
_restore_com PROC FAR
push bp
mov bp,sp
push si
PUSH BX
MOV SI,CURRENT_AREA ; SI POINTS TO DATA AREA
MOV INSTALLED[SI],0 ; PORT IS NO LONGER INSTALLED
MOV AH,25H ; SET INTERRUPT VECTOR FUNCTION
MOV AL,INT_COM[SI] ; INTERRUPT NUMBER
MOV DX,OLD_COM_OFF[SI] ; OLD OFFSET TO DX
MOV BX,OLD_COM_SEG[SI] ; OLD SEG
PUSH DS ; SAVE DS
MOV DS,BX ; TO DS
INT DOS ; DOS FUNCTION
POP DS ; RECOVER DS
POP BX
pop si
mov sp,bp
pop bp
RET
_restore_com ENDP
PAGE ;
;
; OPEN_COM ON CURRENT PORT
;
; CLEAR BUFFERS
; RE-INITIALIZE THE UART
; ENABLE INTERRUPTS ON THE 8259 INTERRUPT CONTROL CHIP
;
; [bp+6] = BAUD RATE
; [bp+8] = CONNECTION: M(ODEM), D(IRECT)
; [bp+10] = PARITY: N(ONE), O(DD), E(VEN), S(PACE), M(ARK)
; [bp+12] = STOP BITS: 1, 2
; [bp+14] = XON/XOFF: E(NABLED), D(ISABLED)
;
_open_com PROC FAR
push bp
mov bp,sp
push si
PUSHF
PUSH BX
PUSH DX
MOV SI,CURRENT_AREA ; SI POINTS TO DATA AREA
CLI ; INTERRUPTS OFF
mov ax,[bp+6]
MOV BAUD_RATE[SI],AX ; SET
mov bh,[bp+8]
MOV CONNECTION[SI],BH ; ARGS
mov bl,[bp+10]
MOV PARITY[SI],BL ; IN
mov ch,[bp+12]
MOV STOP_BITS[SI],CH ; MEMORY
mov cl,[bp+14]
MOV XON_XOFF[SI],CL
; RESET FLOW CONTROL
MOV HOST_OFF[SI],0 ; HOST FLOWING
MOV PC_OFF[SI],0 ; PC FLOWING
MOV URGENT_SEND[SI],0 ; No flow ctl which must go out
MOV SEND_OK[SI],0 ; DTR&CTS are not on yet
; RESET BUFFER COUNTS AND POINTERS
MOV START_TDATA[SI],0
MOV END_TDATA[SI],0
MOV START_RDATA[SI],0
MOV END_RDATA[SI],0
MOV SIZE_TDATA[SI],0
MOV SIZE_RDATA[SI],0
TEST INSTALLED[SI],1 ; PORT INSTALLED?
JNZ OC1 ; SKIP IF SO
JMP OCX ; ELSE ABORT
; RESET THE UART
OC1: MOV DX,MCR[SI] ; Modem Control Register
MOV AL,0 ; Clr DTR, RTS, OUT1, OUT2 & LOOPBACK
OUT DX,AL
MOV AL,30H ; Be sure DSR and CTS are ON. Some
MOV DX,MSR[SI] ; modems need this (I don't know why!)
OUT DX,AL ; Clear Loopback, etc.
JMP $+2 ; I/O DELAY FOR JR
IN AL,DX ; Read to get the delta bits & clr int
AND AL,30H ; Leave the two critical bits
CMP AL,30H ; Both on?
JNE OC2 ; No. Leave SEND_OK zero.
MOV SEND_OK[SI],1 ; Allow TX_INT to send out data
OC2: MOV DX,FCR[SI] ; I/O Address of FIFO control register
MOV AL,FIFO_CLEAR ; Disable FIFOs
OUT DX,AL ; Non-16550A chips will ignore this
MOV DX,LSR[SI] ; RESET LINE STATUS CONDITION
IN AL,DX
JMP $+2 ; I/O DELAY FOR JR
MOV DX,DATREG[SI] ; RESET RECEIVE DATA CONDITION
IN AL,DX
JMP $+2 ; I/O DELAY FOR JR
MOV DX,MSR[SI] ; RESET MODEM DELTAS AND CONDITIONS
IN AL,DX
; CONVERT PASSED BAUD RATE TO UART DIVISOR
MOV AX,50 ; 50 BAUD
MUL DIV50 ; TIMES ITS DIVISOR
DIV BAUD_RATE[SI] ; OTHER SPEEDS ARE PROPORTIONAL
MOV BX,AX ; RESULT TO BX
; SET UART DIVISOR
MOV DX,LCR[SI] ; LINE CONTROL REGISTER
MOV AL,80H ; HI BIT ON
OUT DX,AL ; SET DLAB = 1
JMP $+2 ; I/O DELAY FOR JR
MOV DX,WORD PTR DLL[SI] ; LEAST SIGNIFICANT BYTE
MOV AL,BL ; LSB FROM TABLE
OUT DX,AL ; SET LSB ON UART
JMP $+2 ; I/O DELAY FOR JR
MOV DX,WORD PTR DLH[SI] ; MOST SIGNIFICANT BYTE
MOV AL,BH ; MSB FROM TABLE
OUT DX,AL ; SET MSB ON UART
JMP $+2 ; I/O DELAY FOR JR
; SET PARITY AND NUMBER OF STOP BITS
MOV AL,03H ; NONE OR SPACE PARITY IS THE DEFAULT
CMP PARITY[SI],'O' ; ODD PARITY REQUESTED?
JNE P1 ; JUMP IF NOT
MOV AL,0AH ; SELECT ODD PARITY
JMP SHORT P3 ; CONTINUE
;
P1: CMP PARITY[SI],'E' ; EVEN PARITY REQUESTED?
JNE P2 ; JUMP IF NOT
MOV AL,1AH ; SELECT EVEN PARITY
JMP SHORT P3 ; CONTINUE
;
P2: CMP PARITY[SI],'M' ; MARK PARITY REQUESTED?
JNE P3 ; JUMP IF NOT
MOV AL,2AH ; SELECT MARK PARITY
P3: TEST STOP_BITS[SI],2 ; 2 STOP BITS REQUESTED?
JZ STOP1 ; NO
OR AL,4 ; YES
STOP1: MOV DX,LCR[SI] ; LINE CONTROL REGISTER
OUT DX,AL ; SET UART PARITY MODE AND DLAB=0
; Initialize the FIFOs
MOV DX,FCR[SI] ; I/O Address of FIFO control register
MOV AL,FIFO_INIT ; Clear FIFOs, set size, enable FIFOs
OUT DX,AL ; Non-16550A chips will ignore this
; ENABLE INTERRUPTS ON 8259 AND UART
IN AL,INTA01 ; SET ENABLE BIT ON 8259
AND AL,NIRQ[SI]
OUT INTA01,AL
MOV DX,IER[SI] ; Interrupt enable register
MOV AL,0FH ; Line & Modem status, recv, send
OUT DX,AL ; Enable all interrupts
JMP $+2 ; I/O DELAY FOR JR
OCX: STI ; INTERRUPTS ON
POP DX
POP BX
POPF
pop si
mov sp,bp
pop bp
RET ; DONE
_open_com ENDP
PAGE ;
;
; CLOSE_COM - TURNS OFF INTERRUPTS FROM THE COMMUNICATIONS PORT
;
_close_com PROC FAR
push bp
mov bp,sp
push si
PUSH DX
MOV SI,CURRENT_AREA ; SI POINTS TO DATA AREA
TEST INSTALLED[SI],1 ; PORT INSTALLED?
JZ CCX ; ABORT IF NOT
; TURN OFF UART and clear FIFOs in NS16550A
MOV DX,IER[SI]
MOV AL,0
OUT DX,AL ; No interrupts right now, please
MOV AL,FIFO_CLEAR
OUT DX,AL
; TURN OFF 8259
MOV DX,INTA01
IN AL,DX
OR AL,IRQ[SI]
JMP $+2 ; DELAY FOR AT
OUT DX,AL
CCX: POP DX
pop si
mov sp,bp
pop bp
RET
_close_com ENDP
PAGE ;
;
; DTR_OFF - TURNS OFF DTR TO TELL MODEMS THAT THE TERMINAL HAS GONE AWAY
; AND TO HANG UP THE PHONE
;
_dtr_off PROC FAR
push bp
mov bp,sp
push si
PUSHF ; SAVE FLAGS
PUSH AX ; SAVE REGS
PUSH DX
PUSH SI
MOV SI,CURRENT_AREA ; SI POINTS TO DATA AREA
TEST INSTALLED[SI],1 ; PORT INSTALLED?
JZ DFX ; ABORT IF NOT
MOV DX,MCR[SI] ; Modem Control Register
IN AL,DX ; See if it is on
AND AL,1 ; Just look at the DTR bit
JE DFX ; Not on. Don't clr. Don't wait.
MOV AL,08H ; DTR off, RTS off, OUT2 on
OUT DX,AL
DFX: POP SI ; RECOVER REGS
POP DX
POP AX
POPF ; RECOVER FLAGS
pop si
mov sp,bp
pop bp
RET
_dtr_off ENDP
PAGE ;
;
; DTR_ON - TURNS DTR ON
;
_dtr_on PROC FAR
push bp
mov bp,sp
push si
PUSHF ; SAVE FLAGS
PUSH AX ; SAVE REGS
PUSH BX
PUSH DX
PUSH SI
MOV SI,CURRENT_AREA ; SI POINTS TO DATA AREA
TEST INSTALLED[SI],1 ; PORT INSTALLED?
JZ DTRONF ; Suppress output if not
; Tell modem we are ready and want to send with line idle
MOV DX,MCR[SI] ; Modem Control Register
MOV AL,00001011B ; OUT 2, RTS, DTR
OUT DX,AL
JMP $+2 ; I/O DELAY FOR JR
; Wait for awhile to give the modem time to respond
MOV AH,2CH ; Get time (H:M:S:H to CH:CL:DH:DL)
INT 21H
MOV BX,DX ; Save seconds&hundreths
ADD BH,05 ; Allow 5 seconds wait (actually 4-6)
CMP BH,60 ; Wrap around check
JL DTRON1 ; No wrap
SUB BH,60
DTRON1: CMP SEND_OK[SI],1 ; Did the modem come up?
JE DTRONS ; Yes. Both DSR and CTS are true.
INT 21H ; Get the time again
CMP DX,BX ; Current time is passed the deadline?
JB DTRON1 ; No, keep checking 'til time runs out
MOV AX,200H ; 2 Seconds
CALL WaitN ; V.24 says 2 sec hi before data
CMP CONNECTION[SI],'D' ; Direct connection (no DSR,CTS)?
JE DTRONS ; Skip DSR, CTS check
; Modem failed to come up. Bump counts that tell why.
MOV DX,MSR[SI] ; MODEM STATUS REGISTER
IN AL,DX ; GET MODEM STATUS
TEST AL,20H ; DATA SET READY?
JNZ DTRON6 ; Yup.
INC WORD PTR EDSR[SI] ; BUMP ERROR COUNT
DTRON6: TEST AL,10H ; Clear To Send?
JNZ DTRONF ; That's OK.
INC WORD PTR ECTS[SI] ; BUMP ERROR COUNT - WE TIMED OUT
; Fall into DTRONF
; Failure return
DTRONF: MOV SEND_OK[SI],0 ; Block output data flow at TX_INT
JMP SHORT DTRONX
; Successful return
DTRONS: MOV SEND_OK[SI],1 ; Enable output
; Fall into DTRONX
DTRONX: POP SI ; RECOVER REGS
POP DX
POP BX
POP AX
POPF ; RECOVER FLAGS
pop si
mov sp,bp
pop bp
RET
_dtr_on ENDP
PAGE ;
;
; Wait for specified time using the 18.2 ticks/second clock
;
; Call: AX has seconds,hundreths
; CALL WaitN
; Return: At least the requested time has passed
;
WaitN PROC NEAR
PUSH BP
MOV BP,SP
PUSH AX
PUSH BX
PUSH CX
PUSH DX
PUSH AX ; Save a copy of the arg
MOV AH,2CH ; Get time (H:M:S:H to CH:CL:DH:DL)
INT DOS
POP BX ; Recover S:H arg
ADD BX,DX ; Determine deadline
CMP BL,100 ; Wrap around?
JL WaitN1 ; No
SUB BL,100 ; Yes. Subtract 100 hundreths
INC BH ; And add a second
WaitN1: CMP BH,60 ; Wrap around check
JL WaitN2 ; No wrap
SUB BH,60 ; Forget about Days and Hours
WaitN2: INT DOS ; Get the time again
CMP DX,BX ; Is current time after the deadline?
JB WaitN2 ; No, keep checking 'til time runs out
POP DX
POP CX
POP BX
POP CX
MOV SP,BP
POP BP
RET
WaitN ENDP
PAGE ;
;
; R_COUNT - RETURNS NUMBER OF BYTES IN THE RECEIVE BUFFER IN AX
; total in DX
;
_r_count PROC FAR
push bp
mov bp,sp
push si
PUSHF
PUSH SI
MOV SI,CURRENT_AREA ; SI POINTS TO DATA AREA
MOV AX,0 ; NOTHING RECEIVED IF NOT INSTALLED
mov dx,R_SIZE
TEST INSTALLED[SI],1 ; PORT INSTALLED?
JZ RCX ; ABORT IF NOT
MOV AX,SIZE_RDATA[SI] ; GET NUMBER OF BYTES USED
RCX: POP SI
POPF
pop si
mov sp,bp
pop bp
RET
_r_count ENDP
PAGE ;
;
; RECEIVE - RETURNS THE NEXT CHARACTER FROM THE RECEIVE BUFFER IN AL
; AND REMOVES IT FROM THE BUFFER
; THE PARITY BIT IS STRIPPED OFF if not in Parity mode
; Returns AX: -1 if port not installed or no characters available
; or AX: the next character
;
_receive_com PROC FAR
push bp
mov bp,sp
push si
PUSHF ; SAVE FLAGS
PUSH BX ; SAVE REGS
PUSH SI
MOV SI,CURRENT_AREA ; SI POINTS TO DATA AREA
mov ax,-1 ; -1 if bad call
TEST INSTALLED[SI],1 ; PORT INSTALLED?
JZ RCVX ; ABORT IF NOT
CMP SIZE_RDATA[SI],0 ; ANY CHARACTERS?
JE RCVX ; ABORT IF NOT
; GOOD CALL
mov ah,0 ; good call
MOV BX,START_RDATA[SI] ; GET POINTER TO OLDEST CHAR
MOV AL,RDATA[SI][BX] ; GET CHAR FROM BUFFER
CMP PARITY[SI],'N' ; ARE WE RUNNING WITH NO PARITY? LBA
JE L11 ; IF SO, DON'T STRIP HIGH BIT LBA
AND AL,7FH ; STRIP PARITY BIT
L11: INC BX ; BUMP START_RDATA
AND BX,R_SIZE-1 ; Ring the pointer
MOV START_RDATA[SI],BX ; SAVE THE NEW START_RDATA VALUE
DEC SIZE_RDATA[SI] ; ONE LESS CHARACTER
CMP XON_XOFF[SI],'E' ; FLOW CONTROL ENABLED?
JNE RCVX ; DO NOTHING IF DISABLED
CMP HOST_OFF[SI],1 ; HOST TURNED OFF?
JNE RCVX ; JUMP IF NOT
CMP SIZE_RDATA[SI],R_SIZE/16; RECEIVE BUFFER NEARLY EMPTY?
JGE RCVX ; DONE IF NOT
MOV HOST_OFF[SI],0 ; TURN ON HOST IF SO
PUSH AX ; SAVE RECEIVED CHAR
MOV AL,CONTROL_Q ; TELL HIM TO TALK
LI3: CLI ; TURN OFF INTERRUPTS
CMP URGENT_SEND[SI],1 ; Previous send still in progress?
JNE LI4 ; No. There is space now.
STI ; Yes. Wait for interrupt to take it.
JMP SHORT LI3 ; Loop 'til it's gone
LI4: CALL SENDII ; SEND IMMEDIATELY INTERNAL
STI ; INTERRUPTS BACK ON
POP AX ; RESTORE RECEIVED CHAR
RCVX: POP SI ; RECOVER REGS
POP BX
POPF ; RECOVER FLAGS
pop si
mov sp,bp
pop bp
RET
_receive_com ENDP
PAGE ;
;
; S_COUNT - RETURNS IN AX THE AMOUNT OF FREE SPACE
; REMAINING IN THE TRANSMIT BUFFER
; DX total size
; N.B. The free space might be negative (-1) if the buffer was full and then
; the program called SENDI or RX_INT required sending a control-S to squelch
; the remote sender. Return 0 in this case.
;
_s_count PROC FAR
push bp
mov bp,sp
push si
PUSHF
PUSH SI
MOV SI,CURRENT_AREA ; SI POINTS TO DATA AREA
MOV AX,0 ; NO SPACE LEFT IF NOT INSTALLED
mov dx,S_SIZE-1 ; Leave 1 byte for a SENDII call
TEST INSTALLED[SI],1 ; PORT INSTALLED?
JZ SCX ; ABORT IF NOT
MOV AX,S_SIZE-1 ; GET THE SIZE OF THE X-MIT BUFFER
SUB AX,SIZE_TDATA[SI] ; SUBTRACT THE NUMBER OF BYTES USED
JGE SCX ; Avoid returning negative number
XOR AX,AX ; Return 0
SCX: POP SI ; RECOVER SI
POPF ; RESTORE FLAGS
pop si
mov sp,bp
pop bp
RET
_s_count ENDP
PAGE ;
;
; SEND - SEND A CHARACTER
; [bp+6] = char
;
_send_com PROC FAR
push bp
mov bp,sp
push si
PUSHF ; SAVE FLAGS
PUSH AX ; SAVE REGS
PUSH BX
PUSH DX
PUSH SI
MOV SI,CURRENT_AREA ; SI POINTS TO DATA AREA
TEST INSTALLED[SI],1 ; PORT INSTALLED?
JZ L44 ; ABORT IF NOT
SENDC1: CMP SIZE_TDATA[SI],S_SIZE-1 ; BUFFER FULL? (Leave room for SENDII)
JGE SENDC1 ; Wait for interrupts to empty buffer
L4A: MOV BX,END_TDATA[SI] ; BX POINTS TO FREE SPACE
MOV AL,[BP+6] ; Character to send
MOV TDATA[SI][BX],AL ; MOVE CHAR TO BUFFER
INC BX ; INCREMENT END_TDATA
AND BX,S_SIZE-1 ; Ring the pointer
MOV END_TDATA[SI],BX ; SAVE NEW END_TDATA
INC SIZE_TDATA[SI] ; ONE MORE CHARACTER IN X-MIT BUFFER
MOV DX,IER[SI] ; INTERRUPT ENABLE REGISTER
IN AL,DX ; GET IT
TEST AL,2 ; SEE IF TX INTERRUPTS ARE ENABLED
JNZ L44 ; JUMP IF SO
CMP SEND_OK[SI],1 ; See if Data Set Ready & CTS are on
JNE L44 ; No. Still can't enable TX ints
TEST PC_OFF[SI],1 ; Were we stopped by a ^S from host?
JNZ L44 ; Yes. Don't enable interrupts yet.
MOV AL,0FH ; IF NOT THEN RCV, TX, LINE & Modem
OUT DX,AL ; ARE ENABLED.
L44: POP SI ; RESTORE REGS
POP DX
POP BX
POP AX
POPF ; RESTORE FLAGS
pop si
mov sp,bp
pop bp
RET ; DONE
_send_com ENDP
PAGE ;
;
; SENDI - SEND A CHARACTER IMMEDIATELY
; [bp+6] = char to send
;
_sendi_com PROC FAR
push bp
mov bp,sp
push si
mov al,[bp+6]
PUSHF ; SAVE FLAGS
PUSH SI
MOV SI,CURRENT_AREA ; SI POINTS TO DATA AREA
TEST INSTALLED[SI],1 ; PORT INSTALLED?
JZ LQ44 ; ABORT IF NOT
SENDI3: CLI ; TURN OFF INTERRUPTS
CMP URGENT_SEND[SI],1 ; Previous send still in progress?
JNE SENDI4 ; No. There is space now.
STI ; Yes. Wait for interrupt to take it.
JMP SHORT SENDI3 ; Loop 'til it's gone
SENDI4: CALL SENDII ; CALL INTERNAL SEND IMMEDIATE
STI ; INTERRRUPTS BACK ON
LQ44: POP SI ; RESTORE REGS
POPF ; RESTORE FLAGS
pop si
mov sp,bp
pop bp
RET ; DONE
_sendi_com ENDP
PAGE ;
;
; INTERNAL ROUTINE -- Called from process level and (receive) interrupt level
; DEPENDS ON CALLER TO KEEP INTERRUPTS CLEARED AND SET SI
; SENDI - SEND A CHARACTER IMMEDIATELY (PUT AT BEGINNING OF QUEUE)
; AL = CHAR TO WRITE
;
SENDII PROC NEAR
PUSH BX
PUSH DX ; SAVE DX
CMP SIZE_TDATA[SI],S_SIZE ; BUFFER FULL?
JB SENDII2 ; JUMP IF NOT
INC WORD PTR EOVFLOW[SI] ; BUMP ERROR COUNT (Can this happen?)
MOV BX,START_TDATA[SI] ; BX POINTS TO FIRST CHAR IN BUFFER
MOV TDATA[SI][BX],AL ; CLOBBER FIRST CHAR IN BUFFER
JMP SHORT SENDII4
SENDII2:MOV BX,START_TDATA[SI] ; BX POINTS TO FIRST CHAR IN BUFFER
DEC BX ; BACKUP THE PTR
CMP BX,-1 ; BEFORE BEGINNING?
JNE SENDII3 ; JUMP IF NOT
MOV BX,S_SIZE-1 ; POINT TO END IF SO
SENDII3:MOV TDATA[SI][BX],AL ; MOVE CHAR TO BUFFER
MOV START_TDATA[SI],BX ; SAVE NEW START_TDATA
INC SIZE_TDATA[SI] ; ONE MORE CHARACTER IN X-MIT BUFFER
MOV URGENT_SEND[SI],1 ; Flag high priority message
SENDII4:MOV DX,IER[SI] ; INTERRUPT ENABLE REGISTER
IN AL,DX ; GET IT
TEST AL,2 ; SEE IF TX INTERRUPTS ARE ENABLED
JNZ SENDIIX ; JUMP IF SO
CMP SEND_OK[SI],1 ; See if Data Set Ready & CTS are on
JNE SENDIIX ; No. Still can't enable TX ints
MOV AL,0FH ; IF NOT THEN RCV, TX, LINE & Modem
OUT DX,AL ; ARE ENABLED
SENDIIX:POP DX ; RECOVER DX
POP BX
RET ; DONE
SENDII ENDP
PAGE ;
;
; S_LOCAL
;
_send_local PROC FAR
push bp
mov bp,sp
push si
PUSHF ; SAVE FLAGS
PUSH AX ; SAVE REGS
PUSH BX
PUSH SI
MOV SI,CURRENT_AREA ; SI POINTS TO DATA AREA
TEST INSTALLED[SI],1 ; PORT INSTALLED?
JZ SLX ; ABORT IF NOT
CLI ; INTERRUPTS OFF
CMP SIZE_RDATA[SI],R_SIZE ; SEE IF ANY ROOM
JB L13A ; SKIP IF ROOM
INC WORD PTR EOVFLOW[SI] ; BUMP OVERFLOW COUNT
JMP SHORT L14 ; PUNT
L13A: MOV BX,END_RDATA[SI] ; BX POINTS TO FREE SPACE
MOV AL,[BP+6] ; Get the byte to send
MOV RDATA[SI][BX],AL ; Put into buffer
INC BX ; INCREMENT END_RDATA POINTER
AND BX,R_SIZE-1 ; Ring the pointer
MOV END_RDATA[SI],BX ; SAVE VALUE
INC SIZE_RDATA[SI] ; GOT ONE MORE CHARACTER
L14: STI ; INTERRUPTS BACK ON
SLX: POP SI ; RECOVER REGS
POP BX
POP AX
POPF ; RECOVER FLAGS
pop si
mov sp,bp
pop bp
RET ; DONE
_send_local ENDP
PAGE ;
;
; BREAK - CAUSES A BREAK TO BE SENT OUT ON THE LINE
;
_break_com PROC FAR
push bp
mov bp,sp
push si
PUSHF
PUSH AX
PUSH BX
PUSH CX
PUSH DX
MOV SI,CURRENT_AREA ; SI POINTS TO DATA AREA
TEST INSTALLED[SI],1 ; PORT INSTALLED?
JZ BREAKX ; ABORT IF NOT
MOV DX,LCR[SI] ; LINE CONTROL REGISTER
IN AL,DX ; GET CURRENT SETTING
JMP $+2 ; I/O DELAY FOR JR
OR AL,40H ; TURN ON BREAK BIT
OUT DX,AL ; SET IT ON THE UART
MOV AX,25 ; 25/100 of a second
CALL WaitN
MOV DX,LCR[SI] ; LINE CONTROL REGISTER
IN AL,DX ; GET CURRENT SETTING
JMP $+2 ; I/O DELAY FOR JR
AND AL,0BFH ; TURN OFF BREAK BIT
OUT DX,AL ; RESTORE LINE CONTROL REGISTER
BREAKX: POP DX ; RECOVER REGS
POP CX
POP BX
POP AX
POPF ; RECOVER FLAGS
pop si
mov sp,bp
pop bp
RET ; DONE
_break_com ENDP
PAGE ;
; COM_ERRORS - RETURN POINTER IN dx:ax TO ERROR COUNTS
;
_com_errors PROC FAR
push bp
mov bp,sp
mov ax,OFFSET DGROUP:CURRENT_AREA
add ax,ERROR_BLOCK
mov dx,ds
mov sp,bp
pop bp
RET
_com_errors ENDP
PAGE ;
;
; _MODEM_STATUS - Returns the modem status register in AL
;
; Bits are: 0x80: -CD (Carrier Detect, inverted)
; 0x40: -RI (Ring Indicator, inverted)
; 0x20: -DSR (Data Set Ready, inverted)
; 0x10: -CTS (Clear to Send, inverted)
; 0x08: Delta Carrier Detect (CD changed)
; 0x04: Trailing edge of RI (RI went OFF)
; 0x02: Delta DSR (DSR changed)
; 0x01: Delta CTS (CTS changed)
;
_modem_status PROC FAR
push bp
mov bp,sp
PUSH DX
PUSH SI
MOV SI,CURRENT_AREA ; Point to block for selected port
MOV DX,MSR[SI] ; IO Addr of Modem Status Register
IN AL,DX ; Get the live value
XOR AH,AH ; Flush unwanted bits
POP SI
POP DX
mov sp,bp
pop bp
RET
_modem_status ENDP
PAGE ;
;
; INT_HNDLR1 - HANDLES INTERRUPTS GENERATED BY COM1:
;
INT_HNDLR1 PROC FAR
PUSH SI
MOV SI,OFFSET DGROUP:AREA1 ; DATA AREA FOR COM1:
JMP SHORT INT_COMMON ; CONTINUE
;
; INT_HNDLR2 - HANDLES INTERRUPTS GENERATED BY COM2:
;
INT_HNDLR2 PROC FAR
PUSH SI
MOV SI,OFFSET DGROUP:AREA2 ; DATA AREA FOR COM2:
JMP SHORT INT_COMMON ; CONTINUE
;
; INT_HNDLR3 - HANDLES INTERRUPTS GENERATED BY COM3:
;
INT_HNDLR3 PROC FAR
PUSH SI
MOV SI,OFFSET DGROUP:AREA3 ; DATA AREA FOR COM3:
JMP SHORT INT_COMMON ; CONTINUE
;
; INT_HNDLR4 - HANDLES INTERRUPTS GENERATED BY COM4:
;
INT_HNDLR4 PROC FAR
PUSH SI
MOV SI,OFFSET DGROUP:AREA4 ; DATA AREA FOR COM4:
; Fall into INT_COMMON
PAGE ;
;
; BODY OF INTERRUPT HANDLER
;
INT_COMMON:
PUSH AX
PUSH BX
PUSH CX
PUSH DX
PUSH DS
MOV AX,DGROUP ; Offsets are relative to DGROUP [WWP]
MOV DS,AX
; CLEAR THE INTERRUPT CONTROLLER FLAG
MOV DX,INTA00 ; 8259 CONTROL PORT
MOV AL,EOI[SI] ; SPECIFIC END OF INTERRUPT
OUT DX,AL ; CLEAR FLAG
; FIND OUT WHERE INTERRUPT CAME FROM AND JUMP TO ROUTINE TO HANDLE IT
REPOLL: MOV DX,IIR[SI] ; Interrupt Identification Register
IN AL,DX
TEST AL,1 ; Check the "no interrupt present" bit
JNZ INT_END ; ON means we are done
MOV BL,AL ; Put where we can index by it
AND BX,000EH ; Ignore FIFO_ENABLED bits, etc.
JMP WORD PTR CS:INT_DISPATCH[BX]; Go to appropriate routine
INT_DISPATCH:
DW MSTAT_INT ; 0: Modem status interrupt
DW TX_INT ; 2: Transmitter interrupt
DW RX_INT ; 4: Receiver interrupt
DW LSTAT_INT ; 6: Line status interrupt
DW REPOLL ; 8: (Future use by UART makers)
DW REPOLL ; A: (Future use by UART makers)
DW RX_INT ; C: FIFO Timeout. Drain data trapped in FIFO.
DW REPOLL ; E: (Future use by UART makers)
INT_END:POP DS
POP DX
POP CX
POP BX
POP AX
POP SI
IRET
PAGE ;
;
; Line status interrupt
;
LSTAT_INT:
MOV DX,LSR[SI] ; Line status register
IN AL,DX ; Read line status & bump error counts
TEST AL,2 ; OVERRUN ERROR?
JZ LSI1 ; JUMP IF NOT
INC WORD PTR EOVRUN[SI] ; ELSE BUMP ERROR COUNT
LSI1: TEST AL,4 ; PARITY ERROR?
JZ LSI2 ; JUMP IF NOT
INC WORD PTR EPARITY[SI] ; ELSE BUMP ERROR COUNT
LSI2: TEST AL,8 ; FRAMING ERROR?
JZ LSI3 ; JUMP IF NOT
INC WORD PTR EFRAME[SI] ; ELSE BUMP ERROR COUNT
LSI3: TEST AL,16 ; BREAK RECEIVED?
JZ LSI4 ; JUMP IF NOT
INC WORD PTR EBREAK[SI] ; ELSE BUMP ERROR COUNT
LSI4: JMP REPOLL ; SEE IF ANY MORE INTERRUPTS
;
; Modem status interrupt
;
MSTAT_INT:
MOV DX,MSR[SI] ; Modem Status Register
IN AL,DX ; Read status & clear interrupt
CMP CONNECTION[SI],'D' ; Direct connection - ignore int
JE MSI0 ; Just noise on DSR,CTS pins
AND AL,30H ; Expose CTS and Data Set Ready
CMP AL,30H ; Both on?
JE MSI0 ; Yes. Enable output at TX_INT
XOR AL,AL
JMP SHORT MSI1
MSI0: MOV AL,1
MSI1: MOV SEND_OK[SI],AL ; Put where TXI and send_com can see
MOV DX,IER[SI] ; Let a TX int happen for thoro chks
MOV AL,0FH ; Line & modem sts, recv, send.
OUT DX,AL
JMP REPOLL ; Check for other interrupts
PAGE ;
;
; Tranmit interrupt
;
TX_INT: CMP SEND_OK[SI],1 ; Harware (CTS & DSR on) OK?
JNE TXI9 ; No. Must wait 'til cable right!
MOV CX,1 ; Transfer count for flow ctl
CMP URGENT_SEND[SI],1 ; Flow control character to send?
JE TXI3 ; Yes. Always send flow control.
CMP PC_OFF[SI],1 ; Flow control (XON/XOFF) OK?
JE TXI9 ; Stifled & not urgent. Forget it.
TXI1: MOV CL,UART_SILO_LEN[SI] ; MAX size chunk (1 for simple 8250)
; Too bad there is no "Tranmitter FIFO Full" indication!
CMP SIZE_TDATA[SI],CX ; SEE IF ANY MORE DATA TO SEND
JG TXI2 ; UART is the limit
MOV CX,SIZE_TDATA[SI] ; Buffer space limited. Use that.
TXI2: JCXZ TXI9 ; No data, disable TX ints
TXI3: MOV BX,START_TDATA[SI] ; BX POINTS TO NEXT CHAR. TO BE SENT
MOV AL,TDATA[SI][BX] ; GET DATA FROM BUFFER
MOV DX,DATREG[SI] ; DX EQUALS PORT TO SEND DATA TO
OUT DX,AL ; SEND DATA
INC BX ; INCREMENT START_TDATA
AND BX,S_SIZE-1 ; Ring the pointer
MOV START_TDATA[SI],BX ; SAVE START_TDATA
DEC SIZE_TDATA[SI] ; ONE LESS CHARACTER IN X-MIT BUFFER
LOOP TXI3 ; Keep going 'til silo is full
MOV URGENT_SEND[SI],0 ; Tell process level we sent flow ctl
JMP REPOLL
; IF NO DATA TO SEND, or can't send, RESET TX INTERRUPT AND RETURN
TXI9: MOV DX,IER[SI]
MOV AL,0DH ; Line & modem sts, recv, no send.
OUT DX,AL
JMP REPOLL
PAGE ;
;
; Receive interrupt
;
RX_INT:
RXI0: MOV DX,LSR[SI] ; Line Status Register
IN AL,DX ; Read it
TEST AL,1 ; Check the RECV DATA READY bit
JE RXIX ; No more data available
MOV DX,DATREG[SI] ; UART DATA REGISTER
IN AL,DX ; Get data, clear status
CMP XON_XOFF[SI],'E' ; FLOW CONTROL ENABLED?
JNE RXI2 ; No. Don't check for XON/XOFF
; Check each character for possible flow control (XON/XOFF)
AND AL,7FH ; STRIP PARITY
CMP AL,CONTROL_S ; STOP COMMAND RECEIVED?
JNE RXI1 ; Jump if not. Might be ^Q though.
MOV PC_OFF[SI],1 ; Stop output
JMP SHORT RXI0 ; Don't store character
RXI1: CMP AL,CONTROL_Q ; GO COMMAND RECEIVED?
JNE RXI2 ; No. Not a flow control character
MOV PC_OFF[SI],0 ; Enable output
JMP SHORT RXI0 ; Don't store character
; Have a real data byte. Store if possible.
RXI2: CMP SIZE_RDATA[SI],R_SIZE ; SEE IF ANY ROOM
JL RXI6 ; CONTINUE IF SO
INC WORD PTR EOVFLOW[SI] ; BUMP OVERFLOW ERROR COUNT
JMP SHORT RXIX
RXI6: MOV BX,END_RDATA[SI] ; BX POINTS TO FREE SPACE
MOV RDATA[SI][BX],AL ; MOVE DATA TO BUFFER
INC SIZE_RDATA[SI] ; GOT ONE MORE CHARACTER
INC BX ; INCREMENT END_RDATA POINTER
AND BX,R_SIZE-1 ; Ring the pointer
MOV END_RDATA[SI],BX ; SAVE VALUE
; See if we must tell remote host to stop outputting.
CMP XON_XOFF[SI],'E' ; FLOW CONTROL ENABLED?
JNE RXIX ; No
CMP HOST_OFF[SI],1 ; Already told remote to shut up?
JE RXIX ; Yes. Don't flood him with ^Ss
CMP SIZE_RDATA[SI],R_SIZE/2 ; RECEIVE BUFFER NEARLY FULL?
JLE RXIX ; No. No need to stifle remote end
; Would like to wait here for URGENT_SEND to go off if it is on.
; But we need to take a TX interrupt for that to happen.
MOV AL,CONTROL_S ; TURN OFF HOST IF SO
CALL SENDII ; SEND IMMEDIATELY INTERNAL
MOV HOST_OFF[SI],1 ; HOST IS NOW OFF
RXIX: JMP REPOLL
INT_HNDLR4 ENDP
INT_HNDLR3 ENDP
INT_HNDLR2 ENDP
INT_HNDLR1 ENDP
COM_TEXT ENDS
END